package com.xjh.utils.beanutils.internal.reflect;

import com.xjh.utils.beanutils.BeanCopier;
import com.xjh.utils.beanutils.BeanCopyConvertor;
import com.xjh.utils.beanutils.BeanCopyUtils;
import com.xjh.utils.beanutils.annotation.CopyFeature;
import com.xjh.utils.beanutils.exception.BeanCopyException;
import com.xjh.utils.beanutils.internal.cache.BeanCopyPropertyItem;
import com.xjh.utils.beanutils.internal.logger.Logger;
import com.xjh.utils.beanutils.internal.utils.InstanceUtils;

import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.List;

/**
 * Bean反射复制
 *
 * @company 湖南省安第斯信息科技有限公司, 版权所有
 * @author kevin
 * @date 2021/3/10
 */
@SuppressWarnings("rawtypes")
public class ReflectBeanCopy implements BeanCopier {
	
	private static Logger logger = Logger.getLogger(BeanCopyUtils.class);

	private List<BeanCopyPropertyItem> items ;
	private CopyFeature[] features;
	
	public ReflectBeanCopy(List<BeanCopyPropertyItem> items, CopyFeature[] features) {
		this.items = items;
		this.features = features;
	}

	@SuppressWarnings( "unchecked")
	public Object copyBean(Object sourceObject, Object targetObject) {
		
		for( BeanCopyPropertyItem item: items ) {
			
			Class<?> targetPropertyType = item.writeMethod.getParameterTypes()[0];
			
			if( item.isCollection ) {
				
				try {
					Object targetValue = sourceObject;
					for( int i =0; i< item.readMethods.length; i++ ) {
						targetValue = item.readMethods[i].invoke(targetValue);
					}
					
					if( item.useBeanCopy ) {
						Object writeObject = null;
						if (item.collectionClass != null )
							writeObject = InstanceUtils.unsafeCopyCollection(targetValue, item.collectionClass, item.optionClass, targetPropertyType, features);
						else
							writeObject = InstanceUtils.unsafeCopyArray(targetValue, targetPropertyType.getComponentType(), item.optionClass, features);
						item.writeMethod.invoke(targetObject, writeObject);
					} else {
						Collection collectionRead = (Collection) targetValue;
						Collection collectionWrite = null;
						
						if(collectionRead!= null ) {
							collectionWrite = InstanceUtils.newCollection(targetPropertyType);
							if( collectionWrite == null ) {
								logger.error("beanCopy: cannot copy collection property " + targetPropertyType.getSimpleName() );
								throw new BeanCopyException("beanCopy: cannot copy collection property " + targetPropertyType.getSimpleName() );
							}
							for( Object sourceObj: collectionRead ) {
								Object targetCollectionObject = BeanCopyUtils.copyBean(sourceObj, item.collectionClass, item.optionClass );
								collectionWrite.add(targetCollectionObject);
							}
						}
						
						item.writeMethod.invoke(targetObject, collectionWrite);
					}
					
				} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
					logger.warn("beanCopy: cannot invoke property copy on " + item.writeMethod.getName(), e );
					throw new BeanCopyException("beanCopy: cannot invoke property copy on " + item.writeMethod.getName(), e  );
				}
				
			} else {
				
				try {
					
					Object targetValue = sourceObject;
					for( int i =0; i< item.readMethods.length && targetValue != null; i++ ) {
						targetValue = item.readMethods[i].invoke(targetValue);
					}
					
					Object sourceData = targetValue;
					
					if( item.convertorObject != null ) {
						sourceData = item.convertorObject.convertTo(sourceData);
					}
					else if( item.convertorClass != null ) {
						BeanCopyConvertor convertor = (BeanCopyConvertor) InstanceUtils.newInstance(item.convertorClass);
						sourceData = convertor.convertTo(sourceData);
						
					} else if( item.optionClass != null ) {
						if( sourceData != null ) {
							sourceData = BeanCopyUtils.copyBean(sourceData, targetPropertyType, item.optionClass);
						}
					} else if( item.useBeanCopy ) {
						sourceData = BeanCopyUtils.copyBean(sourceData, targetPropertyType);
					}
					boolean ignoreInvoke = false;
					if( targetPropertyType.isPrimitive() && sourceData == null ) {
						ignoreInvoke = findFeature( CopyFeature.IGNORE_PRIMITIVE_NULL_SOURCE_VALUE );
					}
					if( !ignoreInvoke && sourceData == null) {
						ignoreInvoke = findFeature( CopyFeature.IGNORE_ALL_NULL_SOURCE_VALUE );
					}
					if( !ignoreInvoke ) {
						item.writeMethod.invoke(targetObject, sourceData );
					}
					
				} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | ClassCastException e) {
					logger.warn("beanCopy: cannot invoke property copy on " + item.writeMethod.getName(), e  );
					throw new BeanCopyException("beanCopy: cannot invoke property copy on " + item.writeMethod.getName(), e  );
				}		
			}
		}
		return targetObject;
	}

	private boolean findFeature(CopyFeature feature ) {
		if( features == null || features.length == 0 )
			return false;
		for( CopyFeature f : features ) {
			if( f == feature )
				return true;
		}
		return false;
	}
	
}
